// DIY模块地址: https://shop64022682.taobao.com/
#include <WiFi.h>
#include <WebServer.h>
#include "driver/mcpwm.h"
#include "carIndex.h"
#include "nvs_flash.h"



#define USE_WIFI_MODE_AP // 屏蔽此行，WIFI设置为客户端模式（需要连接路由器），打开此行，WIFI设置为AP模式(ESP32就是一个WIFI热点，不用连接路由器)

#ifdef USE_WIFI_MODE_AP
const char *ssid = "ESP32WifiCar"; // AP模式的WIFI热点名称
const char *password = "12345678";// WIFI密码
#else
// 网络配置
const char *ssid = "myTest"; // 客户端模式下所要连接的WIFI热点名称，如家里使用的无线路由器WIFI热点名称
const char *password = "12345678"; // 客户端模式下所要连接的WIFI密码，如家里使用的无线路由器WIFI密码
#endif

// 舵机配置
const int servoPin = 12;              // GPIO12
const int initAngle = 90;             // 初始角度
int32_t gSg90Offset = 100;
#define CFG_KEY_SG90_OFFSET_A "sg90_ofsset_a"

// 直流电机配置
#define MOTO_RIGHT_A 33
#define MOTO_RIGHT_B 32
#define MOTO_MIN_PWM 70.0    // 后轴电机动作的最小PWM值（阈值）

WebServer server(80);                 // Web服务器端口80

// flash 本地存储相关
void storeCfgInit()
{
    // 初始化 NVS 分区
    esp_err_t err = nvs_flash_init();
    if (err == ESP_ERR_NVS_NO_FREE_PAGES || err == ESP_ERR_NVS_NEW_VERSION_FOUND)
    {
        ESP_ERROR_CHECK(nvs_flash_erase());
        err = nvs_flash_init();
    }
    ESP_ERROR_CHECK(err);
}

int8_t storeCfgGet(char *cfgKey, int32_t *cfgValue)
{
    nvs_handle handle;
    int32_t readValue = 0;
    esp_err_t err = nvs_open("config", NVS_READWRITE, &handle); // 打开 NVS 分区
    if (err == ESP_OK)
    {

        err = nvs_get_i32(handle, cfgKey, &readValue); // 读取数据
        if (err == ESP_OK)
        {
            *cfgValue = readValue;
            return 0;
        }
        Serial.println("读取本地化参数-nvs_get失败: " + String(err));
    }else{
      Serial.println("读取本地化参数-打开NVS失败: " + String(err));
    }
    *cfgValue = readValue;
    return -1;
}
void storeCfgSet(char *cfgKey, int32_t cfgValue)
{
    nvs_handle handle;
    esp_err_t err = nvs_open("config", NVS_READWRITE, &handle); // 打开 NVS 分区
    if (err == ESP_OK)
    {
        printf("=========storeCfgSet value %" PRIu32 "\n", cfgValue);
        err = nvs_set_i32(handle, cfgKey, cfgValue); // 写入数据
        err = nvs_commit(handle);                    // 更新数据
    }
}

int sg90_A_set_offset(int val) {
    int32_t offsetTmp = 0;
    storeCfgSet(CFG_KEY_SG90_OFFSET_A, val);
    if (0 == storeCfgGet(CFG_KEY_SG90_OFFSET_A, &offsetTmp))
    {
        gSg90Offset = offsetTmp;
    }
    return 0;
}

// 初始化舵机控制
void initServo() {
  int32_t offsetTmp;
  if (0 == storeCfgGet(CFG_KEY_SG90_OFFSET_A, &offsetTmp))
  {
      gSg90Offset = offsetTmp;
      Serial.println("读取本地化舵机偏移量: " + String(offsetTmp));
  }
  // 配置MCPWM单元
  mcpwm_gpio_init(MCPWM_UNIT_0, MCPWM0A, servoPin); // 使用MCPWM0A通道
  
  // 创建配置结构体并赋值
  mcpwm_config_t pwm_config;
  pwm_config.frequency = 50;         // 50Hz频率 (舵机标准)
  pwm_config.cmpr_a = 0;             // 初始占空比0%
  pwm_config.cmpr_b = 0;             // 不使用B通道
  pwm_config.duty_mode = MCPWM_DUTY_MODE_0;   // 占空比模式0
  pwm_config.counter_mode = MCPWM_UP_COUNTER; // 向上计数模式
  
  mcpwm_init(MCPWM_UNIT_0, MCPWM_TIMER_0, &pwm_config);
  
  // 设置初始角度
  setServoAngle(initAngle);
}

// 设置舵机角度函数
void setServoAngle(int angle) {
  // 角度范围限制 (0-180)
  angle = constrain(angle, 0, 180) ;
  
  // 计算脉冲宽度 (500-2500μs)
  int pulseWidth = map(angle, 0, 180, 500, 2500)+ (gSg90Offset-100);
  
  // 设置脉冲宽度
  mcpwm_set_duty_in_us(MCPWM_UNIT_0, MCPWM_TIMER_0, MCPWM_OPR_A, pulseWidth);
  mcpwm_set_duty_type(MCPWM_UNIT_0, MCPWM_TIMER_0, MCPWM_OPR_A, MCPWM_DUTY_MODE_0);
}

// 初始化直流电机控制
void initMotor() {
  // 配置MCPWM单元
  mcpwm_gpio_init(MCPWM_UNIT_1, MCPWM0A, MOTO_RIGHT_A);
  mcpwm_gpio_init(MCPWM_UNIT_1, MCPWM0B, MOTO_RIGHT_B);
  
  // 创建配置结构体并赋值
  mcpwm_config_t mcpwmConfig;
  mcpwmConfig.frequency = 100000;     // 100kHz频率 (电机控制)
  mcpwmConfig.cmpr_a = 0;             // 初始占空比0%
  mcpwmConfig.cmpr_b = 0;             // 初始占空比0%
  mcpwmConfig.counter_mode = MCPWM_UP_COUNTER; // 向上计数模式
  mcpwmConfig.duty_mode = MCPWM_DUTY_MODE_0;   // 占空比模式0
  
  mcpwm_init(MCPWM_UNIT_1, MCPWM_TIMER_0, &mcpwmConfig);
  
  // 初始状态：停止
  mcpwm_set_signal_high(MCPWM_UNIT_1, MCPWM_TIMER_0, MCPWM_GEN_A);
  mcpwm_set_signal_high(MCPWM_UNIT_1, MCPWM_TIMER_0, MCPWM_GEN_B);
}

// 设置电机状态 (val: 0-200)
void setMotor(int val) {
  // 确保值在0-200范围内
  val = constrain(val, 0, 200);
  float duty = 0.0;
  if(val > 100) {
    // 正转 (100-200)
    duty = (val - 100)*((100-MOTO_MIN_PWM)/100)+MOTO_MIN_PWM; // 转换为0-100%
    mcpwm_set_signal_low(MCPWM_UNIT_1, MCPWM_TIMER_0, MCPWM_GEN_A);
    mcpwm_set_duty(MCPWM_UNIT_1, MCPWM_TIMER_0, MCPWM_GEN_B, duty);
    mcpwm_set_duty_type(MCPWM_UNIT_1, MCPWM_TIMER_0, MCPWM_GEN_B, MCPWM_DUTY_MODE_0);
  } else if (val < 100) {
    // 反转 (0-100)
    duty = (100 - val)*((100-MOTO_MIN_PWM)/100)+MOTO_MIN_PWM; // 转换为0-100%
    mcpwm_set_signal_low(MCPWM_UNIT_1, MCPWM_TIMER_0, MCPWM_GEN_B);
    mcpwm_set_duty(MCPWM_UNIT_1, MCPWM_TIMER_0, MCPWM_GEN_A, duty);
    mcpwm_set_duty_type(MCPWM_UNIT_1, MCPWM_TIMER_0, MCPWM_GEN_A, MCPWM_DUTY_MODE_0);
  } else {
    // 停止 (100)
    mcpwm_set_signal_high(MCPWM_UNIT_1, MCPWM_TIMER_0, MCPWM_GEN_A);
    mcpwm_set_signal_high(MCPWM_UNIT_1, MCPWM_TIMER_0, MCPWM_GEN_B);
  }
  Serial.println("PWM: " + String(duty));
  Serial.println("((100-MOTO_MIN_PWM)/100): " + String(((100-MOTO_MIN_PWM)/100)));
  Serial.println("(100 - val)*((100-MOTO_MIN_PWM)/100): " + String((100 - val)*((100-MOTO_MIN_PWM)/100)));
}

// 处理状态请求
void handleStatus() {
  String json = "{\"position\":" + String(gSg90Offset/10) + 
               ",\"sg90Offset\":" + String(gSg90Offset) + "}";
  server.send(200, "application/json", json);
}

void setup() {
  Serial.begin(115200);
  
  // 初始化舵机和电机
  initServo();
  initMotor();
#ifdef USE_WIFI_MODE_AP
  // 初始化AP模式下WIFI参数
  // 设置小车的IP地址为192.168.0.1，对应浏览器中输入的小车网址192.168.0.1
  // WiFi.softAPConfig(IPAddress("192.168.0.1"), IPAddress("192.168.0.1"), IPAddress("255.255.255.0"));
  WiFi.softAPConfig(IPAddress(192, 168, 0, 1), IPAddress(192, 168, 0, 1), IPAddress(255,255,255,0));
  // 设置WIFI热点名称,密码
  WiFi.softAP(ssid, password);
  // 通过串口打印WIFI参数
  Serial.println("WIFI SSID:");
  Serial.println(ssid);
  Serial.println("WIFI Password:");
  Serial.println(password);  
  Serial.print("Camera Ready! Use 'http://");
  Serial.print(WiFi.softAPIP());
  Serial.println("' to connect");
#else    
  // 连接WiFi
  WiFi.begin(ssid, password);
  Serial.print("正在连接到WiFi");
  while (WiFi.status() != WL_CONNECTED) {
    delay(500);
    Serial.print(".");
  }
  Serial.println("\n连接成功!");
  Serial.print("IP地址: ");
  Serial.println(WiFi.localIP());
#endif   
  // 设置服务器路由
  server.on("/", []() {
    server.sendHeader("Content-Type", "text/html; charset=UTF-8");
    server.send(200, "text/html", htmlPage);
  });
  
  server.on("/control", []() {
    if (server.hasArg("type") && server.hasArg("value")) {
      String type = server.arg("type");
      int value = server.arg("value").toInt();
      
      if (type == "servo") {
        if(value>115){
          value = 115;
        }
        if(value<65){
          value = 65;
        }
        setServoAngle(value);
        server.send(200, "text/plain", "舵机角度: " + String(value));
        Serial.println("舵机角度: " + String(value));
      } 
      else if (type == "motor") {
        setMotor(value);
        server.send(200, "text/plain", "电机占空比: " + String(value));
        Serial.println("电机占空比: " + String(value));
      }
      else if (type == "sg90Offset") {
        sg90_A_set_offset(value);
        setServoAngle(90);
        server.send(200, "text/plain", "sg90Offset: " + String(value));
        Serial.println("设置方向居中微调值: " + String(value));
      }
    }
  });
  server.on("/status", handleStatus);
  // 启动服务器
  server.begin();
  Serial.println("HTTP服务器已启动");
}

void loop() {
  server.handleClient();
  delay(10); // 添加小延迟以保持稳定性
}
